package ee.ut.re;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.LineIterator;

public class FlatStream implements Closeable {
	
	File inFile;
	File outFile;
	private boolean started;
	private LineIterator it;
	private Map<String, Integer> fieldMap;
	
	SortedSet<String> fields = new TreeSet<String>(); 
	{
		fields.add("KIRJELDUS_SAUN");
		fields.add("KIRJELDUS_RODU");
		fields.add("KIRJELDUS_YLDPIND");
		fields.add("LINNAOSA");
		fields.add("HIND");
		fields.add("EDIT_DATE");
		fields.add("PRICE_PER_M2");
		fields.add("KIRJELDUS_KESKKYTE");
		fields.add("KIRJELDUS_ELKYTE");
		fields.add("KIRJELDUS_GAASIKYTE");
		fields.add("KIRJELDUS_AHJUKYTE");
		fields.add("KIRJELDUS_KAMIN");
		fields.add("KIRJELDUS_GARAAZH");
		fields.add("coord");
	}
	
	
	public FlatStream(File mysqlFile) throws IOException {
		it = FileUtils.lineIterator(mysqlFile, "UTF-8");
		started = false;
		readHeader();
	}
	
	private void readHeader() {

		while (it.hasNext()) {
			String line = it.next();
			if (!started) {
				if (line.startsWith("INSERT INTO")) {
					started = true;

					fieldMap = extractFieldMap(line);
					return;
				}
				continue;
			}
		}
		
		throw new RuntimeException("End of file hit without encoutering header");
			
	}

	public boolean hasNext() {
		return it.hasNext();
	}
	
	@Override
	public void close() {
		this.it.close();
	}
	
	public Flat next() {
		final String line = it.next();
		if (line.startsWith("INSERT INTO")) {
			return next();
		}
		try {
			SortedMap<String, String> keyFields = extractKeyFields(fieldMap, fields, splitLine(line, fieldMap.size()));
			return mapToFlat(keyFields);
		} catch (Exception x) {
			//x.printStackTrace();
			//System.err.println("Dropped");
			return next();
		}
	}
	
	private Flat mapToFlat(Map<String, String> fields) throws NumberFormatException, ParseException {
		return new Flat(Double.valueOf(fields.get("HIND")),
						Long.valueOf(fields.get("KIRJELDUS_YLDPIND")), 
						booleanValue(fields.get("KIRJELDUS_SAUN")), 
						booleanValue(fields.get("KIRJELDUS_RODU")),
						stringValue(fields.get("LINNAOSA")),
						dateValue(fields.get("EDIT_DATE")),
						Double.valueOf(fields.get("PRICE_PER_M2")),
						stringValue(fields.get("coord")),
						booleanValue(fields.get("KIRJELDUS_KESKKYTE")),
						booleanValue(fields.get("KIRJELDUS_ELKYTE")),
						booleanValue(fields.get("KIRJELDUS_GAASIKYTE")),
						booleanValue(fields.get("KIRJELDUS_AHJUKYTE")),
						booleanValue(fields.get("KIRJELDUS_KAMIN")),
						booleanValue(fields.get("KIRJELDUS_GARAAZH"))); 
	}

	final static private DateFormat DATE_FORMAT = new SimpleDateFormat("''yyyy-MM-dd HH:mm:ss''");
	
	private static Date dateValue(String string) throws ParseException {
		return DATE_FORMAT.parse(string);
	}

	private static String stringValue(String string) {
		string = string.trim();
		if (string.charAt(0) == '\'' && string.charAt(string.length()-1) == '\'') {
			string = string.substring(1, string.length()-1);
		}
		return string;
	}

	private boolean booleanValue(String string) {
		return "1".equals(string);
	}

	private List<String> splitLine(String line, int cnt) {
		line = line.substring(1, line.length()-2);
		Pattern pattern = Pattern.compile("(\\d+|'.*?'|NULL)");
		line = line.replace("\\", "");
		line = line.replaceAll("\\(", "");
		line = line.replaceAll("\\)", "");
		line = line.replaceAll("\\*", "");
		line = line.replaceAll("V''GA", "VAGA");
		line = line.replaceAll("\\+", "");
		line = line.replaceAll("\\[", "");
		line = line.replaceAll("\\]", "");
		line = line.replaceAll("\\?", "");
		line = line.replaceAll("<", "");
		line = line.replaceAll(">", "");
		line = line.replace("/", "");
		line = line.replace("\r\n", "");
		
		List<String> out = new ArrayList<String>();
		for (int i = 0; i < cnt; i++) {
			Matcher matcher = pattern.matcher(line);
			if (matcher.find()){
				String m = matcher.group(1);
				
				out.add(m);
				line = line.replaceFirst(m, "");
				line = line.length() >= 2 ? line.substring(2) : line;
			} else {
				//System.out.println("Line = " + origLine);
				throw new RuntimeException("Did not find in: " + line);
			}
		}
	
		return out;
	}

	private SortedMap<String, String> extractKeyFields(Map<String, Integer> fieldMap,
			Set<String> fields, List<String> values) {
		
		SortedMap<String,String> out = new TreeMap<String,String>();
		
		for (String f : fields) {
			//System.out.println(f);
			//System.out.println(f + " = " + values.get(fieldMap.get(f)));
			out.put(f, values.get(fieldMap.get(f)));
		}
		
		return out;
	}

	/**
	 * @param line
	 * @return Map of field name to line entry index.
	 */
	private Map<String, Integer> extractFieldMap(String line) {
		line = line.replace("INSERT INTO `city2` (", "");
		line = line.replace(") VALUES", "");
		
		Map<String, Integer> out = new HashMap<String, Integer>();
		
		int i = 0;
		for(String name : line.split(",")) {
			name = name.replaceAll("`", "");
			name = name.trim();
			out.put(name, i++);
		} 
		
		
		return out;
	}
		
	
	public static void main(String[] args) throws ParseException {
		System.out.println(dateValue("2008-12-16 13:14:28"));
	}
}
